package com.spatio.serving.basic.service.template.core.component.invoker;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.spatio.serving.basic.service.template.common.constant.ChatMessageConstants;
import com.spatio.serving.basic.service.template.common.domain.mutliFoodRecordDo;
import com.spatio.serving.basic.service.template.common.dto.dify.AnswerData;
import com.spatio.serving.basic.service.template.common.dto.dify.DifyCommon;
import com.spatio.serving.basic.service.template.common.req.AskSimpleReq;
import com.spatio.serving.basic.service.template.common.req.DifyChatMessageReq;
import com.spatio.serving.basic.service.template.common.util.AnswerProcessUtils;
import com.spatio.serving.basic.service.template.common.util.JsonSerUtil;
import com.spatio.serving.basic.service.template.common.vo.ChatMessageVo;
import com.spatio.serving.basic.service.template.core.component.config.ApplicationConfig;
import com.spatio.serving.basic.service.template.core.component.invoker.config.InvokeProperties;
import com.spatio.serving.basic.service.template.mysql.Result.Result;
import com.spatio.serving.basic.service.template.mysql.mapper.MealMapper;
import com.spatio.serving.common.lib.common.http.SpatioHttpClient;
import com.spatio.serving.common.lib.common.util.JsonUtil;
import com.spatio.serving.common.lib.common.util.LatencyWatch;
import lombok.extern.slf4j.Slf4j;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.CookieSpecs;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.util.EntityUtils;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;


import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.math.BigInteger;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Component
@Slf4j
public class DifyInvoker {

    public static final String MODE_STREAMING = "streaming";

    public static final String TOKEN_BEARER = "Bearer ";

    @Resource
    private InvokeProperties invokeProperties;

    @Resource
    private ApplicationConfig applicationConfig;


//    @Value("${url.dify.chat.message}")
//    private String urlChatMessage;


    @Value("${dify_calories_api}")
    private String urlChatMessage;

//    @Value("${dify_scene_token}")
//    private String difySceneToken;

    @Value("${dify_calories_key}")
    private String difyToken;

    @Value("${dify_calories_recommend_key}")
    private String recommendToken;

    @Value("${dify_calories_open_statement}")
    private String urlOpenStatement;
    @Autowired
    private MealMapper mealMapper;


    public HttpResponse basicChatMessage(String questionText,String image_url, String difyConversationId, String difyToken, Map map) throws IOException {
        DifyChatMessageReq req;
        //map.put("record", "100");
        if(image_url!=null){
            JSONObject fileJson=new JSONObject();
            fileJson.put("type","image");
            fileJson.put("transfer_method","remote_url");
            fileJson.put("url",image_url);
            JSONArray array= new JSONArray();
            array.add(fileJson);
            req = DifyChatMessageReq.builder()
                    .inputs(new HashMap<>())
                    .query("这是我吃的所有食物")
                    .user("abc-123")
                    .conversationId(difyConversationId)
                    .files(array)
                    .responseMode(MODE_STREAMING).build();
        }else {
            req = DifyChatMessageReq.builder()
                    .inputs(map)
                    .query(questionText)
                    .user("abc-123")
                    .conversationId(difyConversationId)
                    .responseMode(MODE_STREAMING).build();
        }

        String url = urlChatMessage;
        HttpResponse response = null;

        try {
            HttpClient httpClient = HttpClients.custom()
                    .setDefaultRequestConfig(requestConfig)
                    .build();
            HttpPost httpPost = new HttpPost(url);
            StringEntity entity = new StringEntity(JsonUtil.toJson(req), ContentType.APPLICATION_JSON);
            httpPost.setEntity(entity);
            httpPost.setHeader("Authorization", TOKEN_BEARER + difyToken);
            httpPost.setHeader("Accept", "text/event-stream");
            LatencyWatch watch = LatencyWatch.start();
            response = httpClient.execute(httpPost);
            log.info("DifyInvoker chatMessages stream execute cost:{} req:{}", watch.cost(), req);
        } catch (Exception e) {
            log.error("difyInvoker chatMessages stream url={} have error:", url, e);
            throw new RuntimeException("Failed to get stream", e);
        }
        if (response == null || response.getStatusLine().getStatusCode() != 200) {
            log.warn("DifyInvoker chatMessagesStream stream response StatusCode:{} ", response.getStatusLine().getStatusCode());
            throw new RuntimeException("Failed to get stream: " + response.getStatusLine().getStatusCode());
        }
        return response;
    }

    RequestConfig requestConfig = RequestConfig.custom()
            .setConnectionRequestTimeout(5000)
            .setConnectTimeout(5000)
            .setSocketTimeout(13000)
            .setCookieSpec(CookieSpecs.STANDARD)
            .build();

//    String getTokenByScene(String scene) {
//        JSONObject sceneToken = JSON.parseObject(difySceneToken);
//        if (sceneToken.containsKey(scene)) {
//            return sceneToken.getString(scene);
//        }
//       return "";
//    }

    public DifyCommon chatMessagesStream(BigInteger userId,String questionText, String url,String difyConversationId, String messageId,HttpServletResponse res) throws IOException {
        //String difyToken = getTokenByScene(scene);
        if(!StringUtils.hasText(difyToken)){
            log.error("DifyInvoker chatMessagesStream scene:{} difyToken is empty");
            throw new RuntimeException("DifyInvoker chatMessagesStream scene difyToken is empty");
        }
        Map<String, Object>map=new HashMap<>();
        HttpResponse response = basicChatMessage(questionText,url, difyConversationId, difyToken,map);
        String line;
        StringBuffer answerSb = new StringBuffer();
        String newDifyConversationId = null;
        String newDifyMessageId=null;
        LatencyWatch watch = LatencyWatch.start();
        //String com = response.getEntity().getContent().toString();
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
            while ((line = reader.readLine()) != null) {
                if (StringUtils.hasText(line) && line.length() > 5) {
                    JSONObject responseJson = JsonUtil.fromJson(line.substring(6), JSONObject.class);
                    String answerLine = responseJson.getString("answer");
                    if (!StringUtils.hasText(answerLine)) {
                        continue;
                    }
                    answerLine = answerLine.replace("```", "");
                    answerLine = answerLine.replace("json", "");
                    answerSb.append(answerLine);
                    if (!StringUtils.hasText(newDifyConversationId) && StringUtils.hasText(responseJson.getString("conversation_id"))) {
                        newDifyConversationId = responseJson.getString("conversation_id");
                    }
                    if (!StringUtils.hasText(newDifyMessageId) && StringUtils.hasText(responseJson.getString("message_id"))) {
                        newDifyMessageId = responseJson.getString("message_id");
                    }
//                    if(answerLine.contains("***")){
//                        res=null;
//                    }
//                    if (res != null && StringUtils.hasText(answerLine)) {
//                        ChatMessageVo messageVo = new ChatMessageVo();
//                        messageVo.setAnswer(answerLine);
//                        messageVo.setEvent(ChatMessageConstants.EVENT_MSG);
//                        //res.getWriter().write(JSONObject.toJSONString(messageVo));
//                        res.getWriter().write("data: " + JSONObject.toJSONString(Result.success(messageVo)) + "\n\n");
//                        res.getWriter().flush();
//                    }

                }
            }
            //判断回答里有没有json，json部分就保存在数据库里，非json部分返回给前端
            log.info("DifyInvoker chatMessagesStreamSSE stream single end cost:{} scene:{}", watch.cost());
        } catch (Exception e) {
            log.error("difyInvoker chatMessagesStream assemble have error:", e);
            throw new RuntimeException("difyInvoker chatMessagesStream assemble have error", e);
        }

        difyConversationId = StringUtils.hasText(difyConversationId) ? difyConversationId : "";
        messageId = StringUtils.hasText(messageId) ? messageId : "";
        log.info("DifyInvoker chatMessages stream get text cost:{} difyConversationId:{} newDifyConversationId:{} answer:{} ", watch.cost(), difyConversationId, newDifyConversationId, answerSb);
        AnswerProcessUtils answerProcessUtils = new AnswerProcessUtils();

        DifyCommon difyCommon = new DifyCommon();
        difyCommon.setConversation_id(newDifyConversationId);
        difyCommon.setMessage_id(newDifyMessageId);
        difyCommon.setUser_id(userId);
        //保存图片url
        boolean saveImage = mealMapper.saveImage(userId,newDifyConversationId,url);
        //获得去除json部分的回答
        JSONArray temp_food = answerProcessUtils.getAnswerJson(answerSb.toString());
        if(temp_food!=null){
            String answerText = answerProcessUtils.getAnswerText(answerSb.toString());
            for(int i = 0;i<temp_food.size();i++){

                LocalDateTime time = LocalDateTime.now();
                boolean flag = mealMapper.food_record(userId,time,newDifyMessageId,
                        newDifyConversationId,temp_food.get(i));
            }
            difyCommon.setAnswer_text(answerText);
            difyCommon.setFood_record(temp_food);

        }else {
            difyCommon.setAnswer_text(answerSb.toString());
        }

//        if(temp_food!=null){
//            difyCommon.setFoodRecord(temp_food);
//            //需要存储difycommon
//            String jsonString=temp_food.toString();
//            //获取时间
//            LocalDateTime localDateTime = LocalDateTime.now();
//            boolean flag = mealMapper.food_record(userId,newDifyConversationId,answerText,jsonString,localDateTime);
//        }
        return difyCommon;
    }


    public void chatMessagesStreamV2(BigInteger userId,String questionText, String url,String difyConversationId, String messageId,HttpServletResponse res ) throws IOException {
        //String difyToken = getTokenByScene(scene);
        if(!StringUtils.hasText(difyToken)){
            log.error("DifyInvoker chatMessagesStream scene:{} difyToken is empty");
            throw new RuntimeException("DifyInvoker chatMessagesStream scene difyToken is empty");
        }
        Map<String, Object>map=new HashMap<>();
        //LocalDateTime start=LocalDateTime.now();
        HttpResponse response = basicChatMessage(questionText,url, difyConversationId, difyToken,map);
        //LocalDateTime end=LocalDateTime.now();
        //Duration duration = Duration.between(start, end);
        String line;
        StringBuffer answerSb = new StringBuffer();
        String newDifyConversationId = null;
        String newDifyMessageId=null;
        LatencyWatch watch = LatencyWatch.start();
        //String com = response.getEntity().getContent().toString();
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
            while ((line = reader.readLine()) != null) {
                if (StringUtils.hasText(line) && line.length() > 5) {
                    JSONObject responseJson = JsonUtil.fromJson(line.substring(6), JSONObject.class);
                    String answerLine = responseJson.getString("answer");
                    if (!StringUtils.hasText(answerLine)) {
                        continue;
                    }
                    answerLine = answerLine.replace("```", "");
                    answerLine = answerLine.replace("json", "");
                    answerSb.append(answerLine);
                    if (!StringUtils.hasText(newDifyConversationId) && StringUtils.hasText(responseJson.getString("conversation_id"))) {
                        newDifyConversationId = responseJson.getString("conversation_id");
                    }
                    if (!StringUtils.hasText(newDifyMessageId) && StringUtils.hasText(responseJson.getString("message_id"))) {
                        newDifyMessageId = responseJson.getString("message_id");
                    }
                    if(answerLine.contains("***")){
                        res=null;
                    }
                    if (res != null && StringUtils.hasText(answerLine)) {
                        ChatMessageVo messageVo = new ChatMessageVo();
                        messageVo.setConversation_id(newDifyConversationId);
                        messageVo.setUser_id(userId);
                        messageVo.setMessage_id(newDifyMessageId);
                        messageVo.setAnswer(answerLine);
                        messageVo.setEvent(ChatMessageConstants.EVENT_MSG);
                        //res.getWriter().write("data: "+messageVo+"\n\n");
                        res.getWriter().write("data: " + JSONObject.toJSONString(Result.success(messageVo)) + "\n\n");
                        res.getWriter().flush();
                        //res.wait(10);
                    }

                }
            }
            //判断回答里有没有json，json部分就保存在数据库里，非json部分返回给前端
            log.info("DifyInvoker chatMessagesStreamSSE stream single end cost:{} scene:{}", watch.cost());
        } catch (Exception e) {
            log.error("difyInvoker chatMessagesStream assemble have error:", e);
            throw new RuntimeException("difyInvoker chatMessagesStream assemble have error", e);
        }

        difyConversationId = StringUtils.hasText(difyConversationId) ? difyConversationId : "";
        messageId = StringUtils.hasText(messageId) ? messageId : "";
        log.info("DifyInvoker chatMessages stream get text cost:{} difyConversationId:{} newDifyConversationId:{} answer:{} ", watch.cost(), difyConversationId, newDifyConversationId, answerSb);
        AnswerProcessUtils answerProcessUtils = new AnswerProcessUtils();

        DifyCommon difyCommon = new DifyCommon();
        difyCommon.setConversation_id(newDifyConversationId);
        difyCommon.setMessage_id(newDifyMessageId);
        difyCommon.setUser_id(userId);
        //保存图片url
        boolean saveImage = mealMapper.saveImage(userId,newDifyConversationId,url);
        //获得去除json部分的回答
        JSONArray temp_food = answerProcessUtils.getAnswerJson(answerSb.toString());
        //test
        if(temp_food!=null){
            String answerText = answerProcessUtils.getAnswerText(answerSb.toString());
            for(int i = 0;i<temp_food.size();i++){

                LocalDateTime time = LocalDateTime.now();
                boolean flag = mealMapper.food_record(userId,time,newDifyMessageId,
                        newDifyConversationId,temp_food.get(i));
            }
            difyCommon.setAnswer_text(answerText);
            difyCommon.setFood_record(temp_food);

        }else {
            difyCommon.setAnswer_text(answerSb.toString());
        }

//        if(temp_food!=null){
//            difyCommon.setFoodRecord(temp_food);
//            //需要存储difycommon
//            String jsonString=temp_food.toString();
//            //获取时间
//            LocalDateTime localDateTime = LocalDateTime.now();
//            boolean flag = mealMapper.food_record(userId,newDifyConversationId,answerText,jsonString,localDateTime);
//        }

    }

    public void afterRecordRecommend(BigInteger userId, AskSimpleReq req, HttpServletResponse res, Map map) throws IOException {

        HttpResponse response = basicChatMessage(req.getQuestionText(),req.getUrl(), req.getDifyConversationId(), recommendToken, map);
        String line;
        StringBuffer answerSb = new StringBuffer();
        String newDifyConversationId = null;
        String newDifyMessageId=null;
        LatencyWatch watch = LatencyWatch.start();
        //String com = response.getEntity().getContent().toString();
        try {
            BufferedReader reader = new BufferedReader(new InputStreamReader(response.getEntity().getContent()));
            while ((line = reader.readLine()) != null) {
                if (StringUtils.hasText(line) && line.length() > 5) {
                    JSONObject responseJson = JsonUtil.fromJson(line.substring(6), JSONObject.class);
                    String answerLine = responseJson.getString("answer");
                    if (!StringUtils.hasText(answerLine)) {
                        continue;
                    }
                    answerLine = answerLine.replace("```", "");
                    answerLine = answerLine.replace("json", "");
                    answerSb.append(answerLine);
                    if (!StringUtils.hasText(newDifyConversationId) && StringUtils.hasText(responseJson.getString("conversation_id"))) {
                        newDifyConversationId = responseJson.getString("conversation_id");
                    }
                    if (!StringUtils.hasText(newDifyMessageId) && StringUtils.hasText(responseJson.getString("message_id"))) {
                        newDifyMessageId = responseJson.getString("message_id");
                    }
                    if (res != null && StringUtils.hasText(answerLine)) {
                        ChatMessageVo messageVo = new ChatMessageVo();
                        messageVo.setAnswer(answerLine);
                        messageVo.setEvent(ChatMessageConstants.EVENT_MSG);
                        //res.getWriter().write(JSONObject.toJSONString(messageVo));
                        res.getWriter().write("data: " + JSONObject.toJSONString(Result.success(messageVo)) + "\n\n");
                        res.getWriter().flush();
                    }

                }
            }
            //判断回答里有没有json，json部分就保存在数据库里，非json部分返回给前端
            log.info("DifyInvoker chatMessagesStreamSSE stream single end cost:{} scene:{}", watch.cost());
        } catch (Exception e) {
            log.error("difyInvoker chatMessagesStream assemble have error:", e);
            throw new RuntimeException("difyInvoker chatMessagesStream assemble have error", e);
        }

    }

    public String getOpenStatement(){
        String url = urlOpenStatement;
        HttpResponse response = null;
        JsonNode jsonResponse = null;
        try {
            HttpClient httpClient = HttpClients.custom()
                    .setDefaultRequestConfig(requestConfig)
                    .build();
            HttpGet httpGet=new HttpGet(url);
            httpGet.setHeader("Authorization", TOKEN_BEARER + difyToken);
            LatencyWatch watch = LatencyWatch.start();
            response = httpClient.execute(httpGet);
            String responseBody = EntityUtils.toString(response.getEntity());

            // 使用 Jackson 将响应的 body 转换为 JSON
            ObjectMapper objectMapper = new ObjectMapper();
            jsonResponse = objectMapper.readTree(responseBody);
           String opening_statement= jsonResponse.path("opening_statement").asText();
           return opening_statement;
        } catch (Exception e) {
            throw new RuntimeException("Failed to get stream", e);
        }
    }

}
